home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / app / measure.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-25  |  23.0 KB  |  824 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * Measure tool
  5.  * Copyright (C) 1999 Sven Neumann <sven@gimp.org>
  6.  *
  7.  * This program is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; either version 2 of the License, or
  10.  * (at your option) any later version.
  11.  *
  12.  * This program is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  * GNU General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU General Public License
  18.  * along with this program; if not, write to the Free Software
  19.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20.  */
  21.  
  22. #include "config.h"
  23.  
  24. #include <stdlib.h>
  25.  
  26. #include <glib.h>
  27.  
  28. #include "apptypes.h"
  29.  
  30. #include "appenv.h"
  31. #include "draw_core.h"
  32. #include "gimpui.h"
  33. #include "info_dialog.h"
  34. #include "measure.h"
  35. #include "undo.h"
  36.  
  37. #include "libgimp/gimpintl.h"
  38. #include "libgimp/gimpmath.h"
  39.  
  40. /*  definitions  */
  41. #define  TARGET         8
  42. #define  ARC_RADIUS     30
  43. #define  ARC_RADIUS_2   1100
  44. #define  STATUSBAR_SIZE 128
  45.  
  46. /*  possible measure functions  */
  47. typedef enum 
  48. {
  49.   CREATING,
  50.   ADDING,
  51.   MOVING,
  52.   MOVING_ALL,
  53.   GUIDING,
  54.   FINISHED
  55. } MeasureFunction;
  56.  
  57. /*  the measure structure  */
  58. typedef struct _MeasureTool MeasureTool;
  59.  
  60. struct _MeasureTool
  61. {
  62.   DrawCore        *core;        /*  draw core                  */
  63.   MeasureFunction  function;    /*  what are we doing?         */
  64.   gint             last_x;      /*  last x coordinate          */
  65.   gint             last_y;      /*  last y coordinate          */
  66.   gint             point;       /*  what are we manipulating?  */
  67.   gint             num_points;  /*  how many points?           */
  68.   gint             x[3];        /*  three x coordinates        */
  69.   gint             y[3];        /*  three y coordinates        */
  70.   gdouble          angle1;      /*  first angle                */
  71.   gdouble          angle2;      /*  second angle               */
  72.   guint            context_id;  /*  for the statusbar          */
  73. };
  74.  
  75. /*  the measure tool options  */
  76. typedef struct _MeasureOptions MeasureOptions;
  77.  
  78. struct _MeasureOptions
  79. {
  80.   ToolOptions  tool_options;
  81.  
  82.   gboolean     use_info_window;
  83.   gboolean     use_info_window_d;
  84.   GtkWidget   *use_info_window_w;
  85. };
  86.  
  87.  
  88. /*  maximum information buffer size  */
  89. #define MAX_INFO_BUF 16
  90.  
  91. static MeasureOptions *measure_tool_options = NULL;
  92.  
  93. /*  the measure tool info window  */
  94. static InfoDialog  *measure_tool_info = NULL;
  95. static gchar        distance_buf [MAX_INFO_BUF];
  96. static gchar        angle_buf    [MAX_INFO_BUF];
  97.  
  98.  
  99. /*  local function prototypes  */
  100. static void   measure_tool_button_press   (Tool *, GdkEventButton *, gpointer);
  101. static void   measure_tool_button_release (Tool *, GdkEventButton *, gpointer);
  102. static void   measure_tool_motion         (Tool *, GdkEventMotion *, gpointer);
  103. static void   measure_tool_cursor_update  (Tool *, GdkEventMotion *, gpointer);
  104. static void   measure_tool_control      (Tool *, ToolAction,       gpointer);
  105.  
  106. static void   measure_tool_info_window_close_callback (GtkWidget *, gpointer);
  107. static void   measure_tool_info_update                (void);
  108.  
  109.  
  110. static void
  111. measure_tool_options_reset (void)
  112. {
  113.   MeasureOptions *options = measure_tool_options;
  114.  
  115.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->use_info_window_w),
  116.                 options->use_info_window_d);
  117. }
  118.  
  119. static MeasureOptions *
  120. measure_tool_options_new (void)
  121. {
  122.   MeasureOptions *options;
  123.   GtkWidget *vbox;
  124.  
  125.   /*  the new measure tool options structure  */
  126.   options = g_new (MeasureOptions, 1);
  127.   tool_options_init ((ToolOptions *) options,
  128.              _("Measure Tool"),
  129.              measure_tool_options_reset);
  130.   options->use_info_window = options->use_info_window_d  = FALSE;
  131.  
  132.     /*  the main vbox  */
  133.   vbox = options->tool_options.main_vbox;
  134.  
  135.   /*  the use_info_window toggle button  */
  136.   options->use_info_window_w =
  137.     gtk_check_button_new_with_label (_("Use Info Window"));
  138.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (options->use_info_window_w),
  139.                 options->use_info_window_d);
  140.   gtk_box_pack_start (GTK_BOX (vbox), options->use_info_window_w, FALSE, FALSE, 0);
  141.   gtk_signal_connect (GTK_OBJECT (options->use_info_window_w), "toggled",
  142.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  143.               &options->use_info_window);
  144.   gtk_widget_show (options->use_info_window_w);
  145.  
  146.   return options;
  147. }
  148.  
  149. static double
  150. measure_get_angle (gint    dx,
  151.            gint    dy,
  152.            gdouble xres,
  153.            gdouble yres)
  154. {
  155.   gdouble angle;
  156.  
  157.   if (dx)
  158.     angle = gimp_rad_to_deg (atan (((double)(dy) / yres) / ((double)(dx) / xres)));
  159.   else if (dy)
  160.     angle = dy > 0 ? 270.0 : 90.0;
  161.   else
  162.     angle = 180.0;
  163.   
  164.   if (dx > 0)
  165.     {
  166.       if (dy > 0)
  167.     angle = 360.0 - angle;
  168.       else
  169.     angle = -angle;
  170.     }
  171.   else
  172.     angle = 180.0 - angle;
  173.  
  174.   return (angle);
  175. }
  176.  
  177. static void
  178. measure_tool_button_press (Tool           *tool,
  179.                GdkEventButton *bevent,
  180.                gpointer        gdisp_ptr)
  181. {
  182.   GDisplay * gdisp;
  183.   MeasureTool * measure_tool;
  184.   gint x[3];
  185.   gint y[3];
  186.   gint i;
  187.  
  188.   gdisp = (GDisplay *) gdisp_ptr;
  189.   measure_tool = (MeasureTool *) tool->private;
  190.  
  191.   /*  if we are changing displays, pop the statusbar of the old one  */ 
  192.   if (tool->state == ACTIVE && gdisp_ptr != tool->gdisp_ptr)
  193.     {
  194.       GDisplay *old_gdisp = tool->gdisp_ptr;
  195.       gtk_statusbar_pop (GTK_STATUSBAR (old_gdisp->statusbar),
  196.              measure_tool->context_id);
  197.       gtk_statusbar_push (GTK_STATUSBAR (gdisp->statusbar), 
  198.               measure_tool->context_id, (""));
  199.     } 
  200.   
  201.   measure_tool->function = CREATING;
  202.  
  203.   if (tool->state == ACTIVE && gdisp_ptr == tool->gdisp_ptr)
  204.     {
  205.       /*  if the cursor is in one of the handles, 
  206.       the new function will be moving or adding a new point or guide */
  207.       for (i=0; i < measure_tool->num_points; i++)
  208.     {
  209.       gdisplay_transform_coords (gdisp, 
  210.                                      measure_tool->x[i], measure_tool->y[i], 
  211.                      &x[i], &y[i], FALSE);      
  212.       if (bevent->x == CLAMP (bevent->x, x[i] - TARGET, x[i] + TARGET) &&
  213.           bevent->y == CLAMP (bevent->y, y[i] - TARGET, y[i] + TARGET))
  214.         {
  215.           if (bevent->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK))
  216.         {
  217.           gboolean  undo_group;
  218.           Guide    *guide;
  219.  
  220.           undo_group = (bevent->state & GDK_CONTROL_MASK &&
  221.                 bevent->state & GDK_MOD1_MASK &&
  222.                 (measure_tool->y[i] ==
  223.                  CLAMP (measure_tool->y[i],
  224.                     0, gdisp->gimage->height)) &&
  225.                 (measure_tool->x[i] ==
  226.                  CLAMP (measure_tool->x[i],
  227.                     0, gdisp->gimage->width)));
  228.  
  229.           if (undo_group)
  230.             undo_push_group_start (gdisp->gimage, GUIDE_UNDO);
  231.  
  232.           if (bevent->state & GDK_CONTROL_MASK && 
  233.               (measure_tool->y[i] ==
  234.                CLAMP (measure_tool->y[i], 0, gdisp->gimage->height)))
  235.             {
  236.               guide = gimp_image_add_hguide (gdisp->gimage);
  237.               undo_push_guide (gdisp->gimage, guide);
  238.               guide->position = measure_tool->y[i];
  239.               gdisplays_expose_guide (gdisp->gimage, guide);
  240.             }
  241.  
  242.           if (bevent->state & GDK_MOD1_MASK &&
  243.               (measure_tool->x[i] ==
  244.                CLAMP (measure_tool->x[i], 0, gdisp->gimage->width)))
  245.             {
  246.               guide = gimp_image_add_vguide (gdisp->gimage);
  247.               undo_push_guide (gdisp->gimage, guide);
  248.               guide->position = measure_tool->x[i];
  249.               gdisplays_expose_guide (gdisp->gimage, guide);
  250.             }
  251.  
  252.           if (undo_group)
  253.             undo_push_group_end (gdisp->gimage);
  254.  
  255.           gdisplays_flush ();
  256.           measure_tool->function = GUIDING;
  257.           break;
  258.         }
  259.           measure_tool->function = (bevent->state & GDK_SHIFT_MASK) ? ADDING : MOVING;
  260.           measure_tool->point = i;
  261.           break;
  262.         }
  263.     }
  264.       /*  adding to the middle point makes no sense  */
  265.       if (i == 0 && measure_tool->function == ADDING && measure_tool->num_points == 3)
  266.     measure_tool->function = MOVING;
  267.  
  268.       /*  if the function is still CREATING, we are outside the handles  */
  269.       if (measure_tool->function == CREATING) 
  270.     {
  271.       if (measure_tool->num_points > 1 && bevent->state & GDK_MOD1_MASK)
  272.         {
  273.           measure_tool->function = MOVING_ALL;
  274.           gdisplay_untransform_coords (gdisp,  
  275.                                            bevent->x, 
  276.                                            bevent->y, 
  277.                        &measure_tool->last_x, 
  278.                                            &measure_tool->last_y, 
  279.                        TRUE, FALSE);
  280.         }
  281.     }
  282.     }
  283.   
  284.   if (measure_tool->function == CREATING)
  285.     {
  286.       if (tool->state == ACTIVE)
  287.     {
  288.       /* reset everything */
  289.       draw_core_stop (measure_tool->core, tool);
  290.  
  291.       gtk_statusbar_pop (GTK_STATUSBAR (gdisp->statusbar), measure_tool->context_id);
  292.       gtk_statusbar_push (GTK_STATUSBAR (gdisp->statusbar), measure_tool->context_id, "");
  293.  
  294.       distance_buf[0] = '\0';
  295.       angle_buf[0] = '\0';
  296.       if (measure_tool_info)
  297.         measure_tool_info_update ();
  298.     }
  299.       else
  300.     {
  301.       /* initialize the statusbar display */
  302.       measure_tool->context_id = 
  303.         gtk_statusbar_get_context_id (GTK_STATUSBAR (gdisp->statusbar), "measure");
  304.     }
  305.       
  306.       /*  set the first point and go into ADDING mode  */
  307.       gdisplay_untransform_coords (gdisp,  bevent->x, bevent->y, 
  308.                    &measure_tool->x[0], &measure_tool->y[0], 
  309.                                    TRUE, FALSE);
  310.       measure_tool->point = 0;
  311.       measure_tool->num_points = 1;
  312.       measure_tool->function = ADDING;
  313.  
  314.       /*  set the gdisplay  */
  315.       tool->gdisp_ptr = gdisp_ptr;
  316.  
  317.       /*  start drawing the measure tool  */
  318.       draw_core_start (measure_tool->core, gdisp->canvas->window, tool);
  319.     }
  320.  
  321.   /*  create the info window if necessary  */
  322.   if (!measure_tool_info &&
  323.       (measure_tool_options->use_info_window ||
  324.        !GTK_WIDGET_VISIBLE (gdisp->statusarea)))
  325.     {
  326.       measure_tool_info = info_dialog_new (_("Measure Tool"),
  327.                        tools_help_func, NULL);
  328.       info_dialog_add_label (measure_tool_info, _("Distance:"), distance_buf);
  329.       info_dialog_add_label (measure_tool_info, _("Angle:"), angle_buf);
  330.       
  331.       gimp_dialog_create_action_area (GTK_DIALOG (measure_tool_info->shell),
  332.                       
  333.                       _("Close"), measure_tool_info_window_close_callback,
  334.                       measure_tool_info, NULL, NULL, TRUE, FALSE,
  335.                       
  336.                       NULL);
  337.     }
  338.   
  339.   gdk_pointer_grab (gdisp->canvas->window, FALSE,
  340.             GDK_POINTER_MOTION_HINT_MASK |
  341.             GDK_BUTTON1_MOTION_MASK |
  342.             GDK_BUTTON_RELEASE_MASK,
  343.             NULL, NULL, bevent->time);
  344.   tool->state = ACTIVE;
  345.   
  346.   /*  set the pointer to the crosshair,
  347.    *  so one actually sees the cursor position
  348.    */
  349.   gdisplay_install_tool_cursor (gdisp, GIMP_CROSSHAIR_SMALL_CURSOR,
  350.                 MEASURE,
  351.                 CURSOR_MODIFIER_NONE,
  352.                 FALSE);
  353. }
  354.  
  355. static void
  356. measure_tool_button_release (Tool           *tool,
  357.                  GdkEventButton *bevent,
  358.                  gpointer        gdisp_ptr)
  359. {
  360.   GDisplay * gdisp;
  361.   MeasureTool * measure_tool;
  362.  
  363.   gdisp = (GDisplay *) gdisp_ptr;
  364.   measure_tool = (MeasureTool *) tool->private;
  365.   
  366.   measure_tool->function = FINISHED;
  367.  
  368.   gdk_pointer_ungrab (bevent->time);
  369.   gdk_flush ();
  370. }
  371.  
  372. static void
  373. measure_tool_motion (Tool           *tool,
  374.              GdkEventMotion *mevent,
  375.              gpointer        gdisp_ptr)
  376. {
  377.   GDisplay * gdisp;
  378.   MeasureTool * measure_tool;
  379.   gint x, y;
  380.   gint ax, ay;
  381.   gint bx, by;
  382.   gint dx, dy;
  383.   gint i;
  384.   gint tmp;
  385.   gdouble angle;
  386.   gdouble distance;
  387.   gchar status_str[STATUSBAR_SIZE];
  388.  
  389.   gdisp = (GDisplay *) gdisp_ptr;
  390.   measure_tool = (MeasureTool *) tool->private;
  391.  
  392.   /*  undraw the current tool  */
  393.   draw_core_pause (measure_tool->core, tool);
  394.  
  395.   /*  get the coordinates  */
  396.   gdisplay_untransform_coords (gdisp, 
  397.                                mevent->x, mevent->y, &x, &y, TRUE, FALSE);
  398.   
  399.   /*  
  400.    *  A few comments here, because this routine looks quite weird at first ...
  401.    *
  402.    *  The goal is to keep point 0, called the start point, to be always the one 
  403.    *  in the middle or, if there are only two points, the one that is fixed.
  404.    *  The angle is then always measured at this point.
  405.    */
  406.  
  407.   switch (measure_tool->function)
  408.     {
  409.     case ADDING:
  410.       switch (measure_tool->point)
  411.     {
  412.     case 0:  /*  we are adding to the start point  */  
  413.       break;
  414.     case 1:  /*  we are adding to the end point, make it the new start point  */
  415.       tmp = measure_tool->x[0];
  416.       measure_tool->x[0] = measure_tool->x[1];
  417.       measure_tool->x[1] = tmp;
  418.       tmp = measure_tool->y[0];
  419.       measure_tool->y[0] = measure_tool->y[1];
  420.       measure_tool->y[1] = tmp;
  421.       break;
  422.     case 2:  /*  we are adding to the third point, make it the new start point  */
  423.       measure_tool->x[1] = measure_tool->x[0];
  424.       measure_tool->y[1] = measure_tool->y[0];
  425.       measure_tool->x[0] = measure_tool->x[2];
  426.       measure_tool->y[0] = measure_tool->y[2];
  427.       break;
  428.     default:
  429.       break;
  430.     }
  431.       measure_tool->num_points = MIN (measure_tool->num_points + 1, 3);
  432.       measure_tool->point = measure_tool->num_points - 1;
  433.       measure_tool->function = MOVING;
  434.       /*  no, don't break here!  */
  435.  
  436.     case MOVING: 
  437.       /*  if we are moving the start point and only have two, make it the end point  */
  438.       if (measure_tool->num_points == 2 && measure_tool->point == 0)
  439.     {
  440.       tmp = measure_tool->x[0];
  441.       measure_tool->x[0] = measure_tool->x[1];
  442.       measure_tool->x[1] = tmp;
  443.       tmp = measure_tool->y[0];
  444.       measure_tool->y[0] = measure_tool->y[1];
  445.       measure_tool->y[1] = tmp;
  446.       measure_tool->point = 1;
  447.     }
  448.       i = measure_tool->point;
  449.  
  450.       measure_tool->x[i] = x;
  451.       measure_tool->y[i] = y;
  452.  
  453.       /*  restrict to horizontal/vertical movements, if modifiers are pressed */
  454.       if (mevent->state & GDK_MOD1_MASK)
  455.     {
  456.       if (mevent->state & GDK_CONTROL_MASK)
  457.         {
  458.           dx = measure_tool->x[i] - measure_tool->x[0];
  459.           dy = measure_tool->y[i] - measure_tool->y[0];
  460.       
  461.           measure_tool->x[i] = measure_tool->x[0] + 
  462.         (dx > 0 ? MAX (abs (dx), abs (dy)) : - MAX (abs (dx), abs (dy)));
  463.           measure_tool->y[i] = measure_tool->y[0] + 
  464.         (dy > 0  ? MAX (abs (dx), abs (dy)) : - MAX (abs (dx), abs (dy)));
  465.         }
  466.       else
  467.         measure_tool->x[i] = measure_tool->x[0];
  468.     }
  469.       else if (mevent->state & GDK_CONTROL_MASK)
  470.     measure_tool->y[i] = measure_tool->y[0];
  471.       break;
  472.  
  473.     case MOVING_ALL:
  474.       dx = x - measure_tool->last_x;
  475.       dy = y - measure_tool->last_y;
  476.       for (i = 0; i < measure_tool->num_points; i++)
  477.     {
  478.       measure_tool->x[i] += dx;
  479.       measure_tool->y[i] += dy;
  480.     }
  481.       measure_tool->last_x = x;
  482.       measure_tool->last_y = y;
  483.       break;
  484.  
  485.     default:
  486.       break;
  487.     }
  488.  
  489.   if (measure_tool->function == MOVING)
  490.     {
  491.       /*  calculate distance and angle  */
  492.       ax = measure_tool->x[1] - measure_tool->x[0];
  493.       ay = measure_tool->y[1] - measure_tool->y[0];
  494.       
  495.       if (measure_tool->num_points == 3)
  496.     {
  497.       bx = measure_tool->x[2] - measure_tool->x[0];
  498.       by = measure_tool->y[2] - measure_tool->y[0];
  499.     }
  500.       else
  501.     {
  502.       bx = 0;
  503.       by = 0;
  504.     }
  505.       
  506.       if (gdisp->dot_for_dot)
  507.     {
  508.       distance = sqrt (SQR (ax - bx) + SQR (ay - by));
  509.       
  510.       if (measure_tool->num_points != 3)
  511.         bx = ax > 0 ? 1 : -1;
  512.       
  513.       measure_tool->angle1 = measure_get_angle (ax, ay, 1.0, 1.0);
  514.       measure_tool->angle2 = measure_get_angle (bx, by, 1.0, 1.0);
  515.       angle = fabs (measure_tool->angle1 - measure_tool->angle2);
  516.       if (angle > 180.0)
  517.         angle = fabs (360.0 - angle);
  518.       
  519.       g_snprintf (status_str, STATUSBAR_SIZE, "%.1f %s, %.2f %s",
  520.               distance, _("pixels"), angle, _("degrees"));
  521.       
  522.       if (measure_tool_options)
  523.         {
  524.           g_snprintf (distance_buf, MAX_INFO_BUF, "%.1f %s", distance, _("pixels"));
  525.           g_snprintf (angle_buf, MAX_INFO_BUF, "%.2f %s", angle, _("degrees"));
  526.         }
  527.     }
  528.       else /* show real world units */
  529.     {
  530.       gchar *format_str = g_strdup_printf ("%%.%df %s, %%.2f %s",
  531.                            gimp_unit_get_digits (gdisp->gimage->unit),
  532.                            gimp_unit_get_symbol (gdisp->gimage->unit),
  533.                            _("degrees"));
  534.       
  535.       distance =  gimp_unit_get_factor (gdisp->gimage->unit) * 
  536.         sqrt (SQR ((gdouble)(ax - bx) / gdisp->gimage->xresolution) +
  537.           SQR ((gdouble)(ay - by) / gdisp->gimage->yresolution));
  538.       
  539.       if (measure_tool->num_points != 3)
  540.         bx = ax > 0 ? 1 : -1;
  541.       
  542.       measure_tool->angle1 = measure_get_angle (ax, ay, 
  543.                             gdisp->gimage->xresolution, 
  544.                             gdisp->gimage->yresolution); 
  545.       measure_tool->angle2 = measure_get_angle (bx, by,
  546.                             gdisp->gimage->xresolution, 
  547.                             gdisp->gimage->yresolution);
  548.       angle = fabs (measure_tool->angle1 - measure_tool->angle2);     
  549.       if (angle > 180.0)
  550.         angle = fabs (360.0 - angle);
  551.       
  552.       g_snprintf (status_str, STATUSBAR_SIZE, format_str, distance , angle);
  553.       g_free (format_str);
  554.       
  555.       if (measure_tool_options)
  556.         {
  557.           gchar *format_str = g_strdup_printf ("%%.%df %s",
  558.                            gimp_unit_get_digits (gdisp->gimage->unit),
  559.                            gimp_unit_get_symbol (gdisp->gimage->unit));
  560.           g_snprintf (distance_buf, MAX_INFO_BUF, format_str, distance);
  561.           g_snprintf (angle_buf, MAX_INFO_BUF, "%.2f %s", angle, _("degrees"));
  562.           g_free (format_str);
  563.         }
  564.     }
  565.       
  566.       /*  show info in statusbar  */
  567.       gtk_statusbar_pop (GTK_STATUSBAR (gdisp->statusbar), measure_tool->context_id);
  568.       gtk_statusbar_push (GTK_STATUSBAR (gdisp->statusbar), measure_tool->context_id,
  569.               status_str);
  570.       
  571.       /*  and in the info window  */
  572.       if (measure_tool_info)
  573.     measure_tool_info_update ();
  574.  
  575.     }  /*  measure_tool->function == MOVING  */
  576.   
  577.   /*  redraw the current tool  */
  578.   draw_core_resume (measure_tool->core, tool);
  579. }
  580.  
  581. static void
  582. measure_tool_cursor_update (Tool           *tool,
  583.                 GdkEventMotion *mevent,
  584.                 gpointer        gdisp_ptr)
  585. {
  586.   MeasureTool   *measure_tool;
  587.   GDisplay      *gdisp;
  588.   gint           x[3];
  589.   gint           y[3];
  590.   gint           i;
  591.   gboolean       in_handle = FALSE;
  592.  
  593.   GdkCursorType  ctype     = GIMP_CROSSHAIR_SMALL_CURSOR;
  594.   CursorModifier cmodifier = CURSOR_MODIFIER_NONE;
  595.  
  596.   gdisp = (GDisplay *) gdisp_ptr;
  597.   measure_tool = (MeasureTool *) tool->private;
  598.  
  599.   if (tool->state == ACTIVE && tool->gdisp_ptr == gdisp_ptr)
  600.     {
  601.       for (i = 0; i < measure_tool->num_points; i++)
  602.     {
  603.       gdisplay_transform_coords (gdisp, 
  604.                                      measure_tool->x[i], measure_tool->y[i], 
  605.                      &x[i], &y[i], FALSE);      
  606.       
  607.       if (mevent->x == CLAMP (mevent->x, x[i] - TARGET, x[i] + TARGET) &&
  608.           mevent->y == CLAMP (mevent->y, y[i] - TARGET, y[i] + TARGET))
  609.         {
  610.           in_handle = TRUE;
  611.           
  612.           if (mevent->state & GDK_CONTROL_MASK)
  613.         {
  614.           if (mevent->state & GDK_MOD1_MASK)
  615.             ctype = GDK_BOTTOM_RIGHT_CORNER;
  616.           else
  617.             ctype = GDK_BOTTOM_SIDE;
  618.           break;
  619.         }
  620.           if (mevent->state & GDK_MOD1_MASK)
  621.         {
  622.           ctype = GDK_RIGHT_SIDE;
  623.           break;
  624.         }
  625.  
  626.           if (mevent->state & GDK_SHIFT_MASK)
  627.         cmodifier = CURSOR_MODIFIER_PLUS;
  628.           else
  629.         cmodifier = CURSOR_MODIFIER_MOVE;
  630.  
  631.           if (i == 0 && measure_tool->num_points == 3 &&
  632.           cmodifier == CURSOR_MODIFIER_PLUS)
  633.         cmodifier = CURSOR_MODIFIER_MOVE;
  634.           break;
  635.         }
  636.     }
  637.  
  638.       if (!in_handle && measure_tool->num_points > 1 &&
  639.       mevent->state & GDK_MOD1_MASK)
  640.     cmodifier = CURSOR_MODIFIER_MOVE;
  641.     }
  642.  
  643.   gdisplay_install_tool_cursor (gdisp, ctype,
  644.                 MEASURE,
  645.                 cmodifier,
  646.                 FALSE);
  647. }
  648.  
  649. static void
  650. measure_tool_draw (Tool *tool)
  651. {
  652.   GDisplay * gdisp;
  653.   MeasureTool * measure_tool;
  654.   gint x[3];
  655.   gint y[3];
  656.   gint i;
  657.   gint angle1, angle2;
  658.   gint draw_arc = 0;
  659.  
  660.   gdisp = (GDisplay *) tool->gdisp_ptr;
  661.   measure_tool = (MeasureTool *) tool->private;
  662.  
  663.   for (i = 0; i < measure_tool->num_points; i++)
  664.     {
  665.       gdisplay_transform_coords (gdisp, measure_tool->x[i], measure_tool->y[i],
  666.                  &x[i], &y[i], FALSE);
  667.       if (i == 0 && measure_tool->num_points == 3)
  668.     {
  669.       gdk_draw_arc (measure_tool->core->win, measure_tool->core->gc, FALSE,
  670.             x[i] - (TARGET >> 1), y[i] - (TARGET >> 1),
  671.             TARGET, TARGET, 0, 23040);
  672.     }
  673.       else
  674.     {
  675.       gdk_draw_line (measure_tool->core->win, measure_tool->core->gc,
  676.              x[i] - TARGET, y[i], 
  677.              x[i] + TARGET, y[i]);
  678.       gdk_draw_line (measure_tool->core->win, measure_tool->core->gc,
  679.              x[i], y[i] - TARGET, 
  680.              x[i], y[i] + TARGET);
  681.     }
  682.       if (i > 0)
  683.     {
  684.       gdk_draw_line (measure_tool->core->win, measure_tool->core->gc,
  685.              x[0], y[0],
  686.              x[i], y[i]);
  687.       /*  only draw the arc if the lines are long enough  */
  688.       if ((SQR (x[i] - x[0]) + SQR (y[i] - y[0])) > ARC_RADIUS_2)
  689.         draw_arc++;
  690.     }
  691.     }
  692.   
  693.   if (measure_tool->num_points > 1 && draw_arc == measure_tool->num_points - 1)
  694.     {
  695.       angle1 = measure_tool->angle2 * 64.0;
  696.       angle2 = (measure_tool->angle1 - measure_tool->angle2) * 64.0;
  697.   
  698.       if (angle2 > 11520)
  699.       angle2 -= 23040;
  700.       if (angle2 < -11520)
  701.       angle2 += 23040;
  702.      
  703.       if (angle2 != 0)
  704.     {
  705.       gdk_draw_arc (measure_tool->core->win, measure_tool->core->gc, FALSE,
  706.             x[0] - ARC_RADIUS, y[0] - ARC_RADIUS,
  707.             2 * ARC_RADIUS, 2 * ARC_RADIUS, 
  708.             angle1, angle2); 
  709.       
  710.       if (measure_tool->num_points == 2)
  711.         gdk_draw_line (measure_tool->core->win, measure_tool->core->gc,
  712.                x[0], y[0],
  713.                x[1] - x[0] <= 0 ? x[0] - ARC_RADIUS - (TARGET >> 1) : 
  714.                                   x[0] + ARC_RADIUS + (TARGET >> 1), 
  715.                y[0]);
  716.     }
  717.     }
  718. }
  719.  
  720. static void
  721. measure_tool_control (Tool       *tool,
  722.               ToolAction  action,
  723.               gpointer    gdisp_ptr)
  724. {
  725.   GDisplay * gdisp;
  726.   MeasureTool * measure_tool;
  727.  
  728.   gdisp = (GDisplay *) tool->gdisp_ptr;
  729.   measure_tool = (MeasureTool *) tool->private;
  730.  
  731.   switch (action)
  732.     {
  733.     case PAUSE:
  734.       draw_core_pause (measure_tool->core, tool);
  735.       break;
  736.  
  737.     case RESUME:
  738.       draw_core_resume (measure_tool->core, tool);
  739.       break;
  740.  
  741.     case HALT:
  742.       gtk_statusbar_pop (GTK_STATUSBAR (gdisp->statusbar), measure_tool->context_id);
  743.       draw_core_stop (measure_tool->core, tool);
  744.       tool->state = INACTIVE;
  745.       break;
  746.  
  747.     default:
  748.       break;
  749.     }
  750. }
  751.  
  752. static void
  753. measure_tool_info_update (void)
  754. {
  755.   info_dialog_update (measure_tool_info);
  756.   info_dialog_popup  (measure_tool_info);
  757. }
  758.  
  759. static void
  760. measure_tool_info_window_close_callback (GtkWidget *widget,
  761.                      gpointer   client_data)
  762. {
  763.   info_dialog_free (measure_tool_info);
  764.   measure_tool_info = NULL;
  765. }
  766.  
  767. Tool *
  768. tools_new_measure_tool (void)
  769. {
  770.   Tool * tool;
  771.   MeasureTool * private;
  772.  
  773.   /*  The tool options  */
  774.   if (! measure_tool_options)
  775.     {
  776.       measure_tool_options = measure_tool_options_new ();
  777.       tools_register (MEASURE, (ToolOptions *) measure_tool_options);
  778.     }
  779.  
  780.   tool = tools_new_tool (MEASURE);
  781.   private = g_new0 (MeasureTool, 1);
  782.  
  783.   private->core       = draw_core_new (measure_tool_draw);
  784.   private->num_points = 0;
  785.   private->function   = CREATING;
  786.  
  787.   tool->private = (void *) private;
  788.  
  789.   tool->preserve = TRUE;  /*  Preserve tool across drawable changes  */
  790.  
  791.   tool->button_press_func   = measure_tool_button_press;
  792.   tool->button_release_func = measure_tool_button_release;
  793.   tool->motion_func         = measure_tool_motion;
  794.   tool->cursor_update_func  = measure_tool_cursor_update;
  795.   tool->control_func        = measure_tool_control;
  796.  
  797.   return tool;
  798. }
  799.  
  800. void
  801. tools_free_measure_tool (Tool *tool)
  802. {
  803.   GDisplay * gdisp;
  804.   MeasureTool * measure_tool;
  805.  
  806.   measure_tool = (MeasureTool *) tool->private;
  807.   gdisp = (GDisplay *) tool->gdisp_ptr;
  808.  
  809.   if (tool->state == ACTIVE)
  810.     {
  811.       gtk_statusbar_pop (GTK_STATUSBAR (gdisp->statusbar), measure_tool->context_id);
  812.       draw_core_stop (measure_tool->core, tool);
  813.     }
  814.  
  815.   draw_core_free (measure_tool->core);
  816.  
  817.   if (measure_tool_info)
  818.     {
  819.       info_dialog_free (measure_tool_info);
  820.       measure_tool_info = NULL;
  821.     }
  822.   g_free (measure_tool);
  823. }
  824.